Load packages

library(Seurat)
library(ggplot2)
library(cowplot)

Load previously saved datasets and add annotation

cdS1 <- readRDS("~/Documents/Experiments1/scRNAseq/Cerebellum_publication/allCells/data/Seurat3.rds")
cdS1@meta.data$dataset <- "E13"
colnames(cdS1@meta.data)[1:2] <- c("nCount_RNA","nFeature_RNA")

cdS2 <- readRDS("./E14/Seurat_E14.rds")
cdS3 <- readRDS("./E15/Seurat_E15.rds")
cdS4 <- readRDS("./E16/Seurat_E16.rds")

cdS1@meta.data$dataset1 <- "E13"
cdS2@meta.data$dataset1 <- "E14"
cdS3@meta.data$dataset1 <- "E15"
cdS4@meta.data$dataset1 <- "E16"

Dataset preprocessing

cdS <- merge(cdS1, c(cdS2, cdS3, cdS4))
cb.list <- SplitObject(object = cdS, split.by = "dataset1")

Normalize data and find variable genes

for (i in 1:length(x = cb.list)) {
  cb.list[[i]] <- NormalizeData(object = cb.list[[i]], verbose = FALSE)
  cb.list[[i]] <- FindVariableFeatures(object = cb.list[[i]], 
                                             selection.method = "vst", nfeatures = 2000, verbose = FALSE)
}

Integrate the deatasets

anchors <- FindIntegrationAnchors(object.list = cb.list, dims = 1:30)
cdS.i <- IntegrateData(anchorset = anchors, dims = 1:30)

switch to integrated assay

DefaultAssay(object = cdS.i) <- "integrated"

Run the standard workflow for visualization and clustering

cdS.i <- ScaleData(object = cdS.i, verbose = FALSE)
cdS.i <- RunPCA(object = cdS.i, npcs = 40, verbose = FALSE)
ElbowPlot(cdS.i, ndims = 40, reduction = "pca")

Find clusters

cdS.i <- FindNeighbors(cdS.i, reduction = "pca", assay = "integrated", dims = 1:23)
Computing nearest neighbor graph
Computing SNN
cdS.i <- FindClusters(cdS.i, modularity.fxn = 1,
                    resolution = 1, algorithm = 1, n.start = 10, n.iter = 10,
                    random.seed = 0, temp.file.location = NULL, edge.file.name = NULL,
                    verbose = F)

Inspect cell clusters

myplots <- list()
for (n in 1:length(res)){
  res1 <- paste("integrated_snn_res",res[n],sep=".")
  Idents(object = cdS.i) <- cdS.i@meta.data[,res1]
  myplots[[n]] <- DimPlot(cdS.i, reduction = "tsne", group.by = "ident", label=T)+
    NoAxes()+NoLegend()
}
myplots1 <- list()
for (n in 1:length(res)){
  res1 <- paste("integrated_snn_res",res[n],sep=".")
  Idents(object = cdS.i) <- cdS.i@meta.data[,res1]
  myplots1[[n]] <- DimPlot(cdS.i, reduction = "umap", group.by = "ident", label=T)+
    NoAxes()+NoLegend()
}
gridExtra::grid.arrange(grobs= myplots, ncol = 2, top = paste0("resolutions: ", res))

gridExtra::grid.arrange(grobs= myplots1, ncol = 2, top = paste0("resolutions: ", res))

res1 <- paste("integrated_snn_res",2,sep=".")
Idents(object = cdS.i) <- cdS.i@meta.data[,res1]

Visualize the results with t-SNE

Visualize the results with UMAP

Visualize the results with UMAP

cdS.i <- RunUMAP(object = cdS.i, dims = 1:23, reduction = "pca",
                      features = NULL, assay = "integrated", nneighbors = 65L, max.dim = 2L,
                      min.dist = 0.4, reduction.name = "umap", reduction.key = "UMAP_",
                      metric = "correlation", seed.use = 42)
DimPlot(cdS.i, reduction = "umap", group.by = "ident", label=T)
DimPlot(cdS.i, reduction = "umap", group.by = "cellType", label=T, split.by = "dataset1")+
  NoAxes()+NoLegend()

DimPlot(cdS.i, reduction = "tsne", group.by = "cellType", label=T, split.by = "dataset1")+
  NoAxes()+NoLegend()

p1 <- DimPlot(cdS.i, reduction = "tsne", group.by = "cellType", label=T, split.by = "dataset1", repel = T)+
  NoAxes()+NoLegend()
p2 = DimPlot(cdS.i, reduction = "umap", group.by = "cellType", label=T, split.by = "dataset1", repel = T)+
  NoAxes()+NoLegend()
p3 <- DimPlot(cdS.i, reduction = "tsne", group.by = "dataset1", label=F)+
  NoAxes()
p4 <- DimPlot(cdS.i, reduction = "tsne", group.by = "ident", label=T)+
  NoAxes()+NoLegend()
p5 = cowplot::plot_grid(p3,p4, rel_widths = c(1,0.8))
p6 <- DimPlot(cdS.i, reduction = "umap", group.by = "dataset1", label=F)+
  NoAxes()
p7 <- DimPlot(cdS.i, reduction = "umap", group.by = "ident", label=T)+
  NoAxes()+NoLegend()
p8 = cowplot::plot_grid(p6,p7, rel_widths = c(1,0.8))
pdf("summary.pdf", h=15, w=10)
cowplot::plot_grid(p5,p1, ncol =1, rel_heights = c(0.9,2))
cowplot::plot_grid(p8,p2, ncol =1, rel_heights = c(0.9,2))
dev.off()
null device 
          1 
p1 <- DimPlot(cdS.i, reduction = "tsne", group.by = "ident", label=T) +
  NoAxes()+NoLegend()
p2 <- DimPlot(cdS.i, reduction = "umap", group.by = "ident", label=T)+
  NoAxes()+NoLegend()
p3 <- DimPlot(cdS.i, reduction = "umap", group.by = "dataset1", label=F)
p4 <- DimPlot(cdS.i, reduction = "umap", group.by = "cellType", label=T)+
  NoAxes()+NoLegend()
cowplot::plot_grid(p1,p2,p3,p4)

p5 <- DimPlot(cdS.i, reduction = "tsne", group.by = "cellType", repel = T, label=T, split.by = "dataset1") +
  NoAxes()+NoLegend()
p6 <- DimPlot(cdS.i, reduction = "umap", group.by = "cellType", repel = T, label=T, split.by = "dataset1")+
  NoAxes()+NoLegend()
cowplot::plot_grid(p5,p6, ncol =1)

Save the Seurat object

saveRDS(file = "Seurat_merge.rds",cdS.i)
mGenes <- readRDS(file="/Volumes/jali/Genome/Annotation1.rds")
sig_genes <- FindAllMarkers(cdS.i, features = NULL,
                            logfc.threshold = 0.25, test.use = "wilcox", min.pct = 0.1,
                            min.diff.pct = -Inf, verbose = F, only.pos = TRUE,
                            max.cells.per.ident = Inf, random.seed = 1, latent.vars = NULL,
                            min.cells.feature = 3, min.cells.group = 3, pseudocount.use = 1,
                            return.thresh = 0.01)

sig_genes <- mutate(sig_genes,pct.diff=pct.1-pct.2)

sig_genes$TF <- "no"
id <- intersect(mGenes$mgi_symbol,sig_genes$gene)

x <- sig_genes[sig_genes$gene %in% id,]
sig_genes$TF[sig_genes$gene %in% id] <- mGenes$Type[match(x$gene,mGenes$mgi_symbol)]
sig_genes$description <- mGenes$description[match(as.character(sig_genes$gene),mGenes$mgi_symbol)]
head(sig_genes)

sig_genes <- sig_genes[, c("gene","TF","description","p_val","p_val_adj","avg_logFC","pct.1","pct.2","pct.diff","cluster")]
openxlsx::write.xlsx(sig_genes,"sigGenes.xlsx")

top <- sig_genes %>% group_by(cluster) %>% top_n(5, avg_logFC);top
tab <- as.data.frame(table(Idents(object = cdS.i)))
top$cellNumber <- tab$Freq[match(top$cluster,tab$Var1)]
top$percentage <- round(top$cellNumber/ncol(cdS.i)*100, digits = 2)
openxlsx::write.xlsx(top,"sigGenes_top5.xlsx")

Annotate cell cluster

Annotate the cell culster in Excel.

top5 <- openxlsx::read.xlsx("sigGenes_sum.xlsx", sheet =2)
top5a <- top5[!duplicated(top5$cluster),]
top5a$cellType
 [1] "PC.Etv1"   "PC.Foxp1"  "PC.Nrgn"   "PC.En1"    "GABA.Pre"  "NPC.S"     "EGL.S"     "BG"        "EGL.S1"    "EGL"       "GC1"       "GABA.Pre1" "C1"        "IN"       
[15] "EGL.Pvalb" "GC2"       "EGL.M"     "NPC.M"     "Tlx3"      "BG"        "PC.Cck"    "GABA.Pro"  "IN"        "lCN"       "GABA.Pro"  "EGL.M1"    "mCN"       "NPC.M"    
[29] "lCN?"      "PC.Cdh9"   "AS"        "AS"        "NPC"       "Peri"      "Endo"      "MidO"      "Micro"     "Eryth"     "Isl1"     
cdS.i@meta.data$cellType1 <- plyr::mapvalues(x = Idents(cdS.i), from = top5a$cluster, to = top5a$cellType)
DimPlot(cdS.i, reduction = "tsne", group.by = "cellType1", label=T, repel = T)+
  NoLegend()

DimPlot(cdS.i, reduction = "umap", group.by = "cellType1", label=T, repel = T)+
  NoLegend()

table(cdS.i@meta.data$cellType1, cdS.i@meta.data$dataset1)
           
            E13 E14 E15 E16
  PC.Etv1   955 482 323  52
  PC.Foxp1  830 488 314  68
  PC.Nrgn   747 482 294  72
  PC.En1    823 326 244  51
  GABA.Pre  542 477 295 113
  NPC.S     457 306 283 173
  EGL.S     197 159 396 440
  BG        480 499 484 342
  EGL.S1    163 136 331 432
  EGL       133 139 365 420
  GC1       153 165 310 395
  GABA.Pre1 311 324 224 111
  C1        306 164 281  91
  IN        319 347 436 298
  EGL.Pvalb 152 150 238 240
  GC2       168  48 189 335
  EGL.M     120  97 232 271
  NPC.M     438 306 277 159
  Tlx3      171 338 140  22
  PC.Cck    298 150 145  20
  GABA.Pro  345 344 277 172
  lCN       138 271  94  32
  EGL.M1     84  58 167 214
  mCN       180 101 111 120
  lCN?      103 181  93  31
  PC.Cdh9   111  90 144  35
  AS        172 187 224 154
  NPC       254  41  48   4
  Peri       41  80  97 109
  Endo       36  63  90  48
  MidO       25  94  85  31
  Micro       4  37  57  54
  Eryth      37   7  22  55
  Isl1       13  57  25   7
p1 <- DimPlot(cdS.i, reduction = "tsne", group.by = "cellType1", label=T, split.by = "dataset1", repel = T)+
  NoAxes()+NoLegend()
p2 = DimPlot(cdS.i, reduction = "umap", group.by = "cellType1", label=T, split.by = "dataset1", repel = T)+
  NoAxes()+NoLegend()
p3 <- DimPlot(cdS.i, reduction = "tsne", group.by = "dataset1", label=F)+
  NoAxes()
p4 <- DimPlot(cdS.i, reduction = "tsne", group.by = "ident", label=T)+
  NoAxes()+NoLegend()
p5 = cowplot::plot_grid(p3,p4, rel_widths = c(1,0.8))
p6 <- DimPlot(cdS.i, reduction = "umap", group.by = "dataset1", label=F)+
  NoAxes()
p7 <- DimPlot(cdS.i, reduction = "umap", group.by = "ident", label=T)+
  NoAxes()+NoLegend()
p8 = cowplot::plot_grid(p6,p7, rel_widths = c(1,0.8))
pdf("summary1.pdf", h=15, w=10)
cowplot::plot_grid(p5,p1, ncol =1, rel_heights = c(0.9,2))
cowplot::plot_grid(p8,p2, ncol =1, rel_heights = c(0.9,2))
dev.off()
null device 
          1 
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBMb2FkIHBhY2thZ2VzCmBgYHtyIGxvYWQtcGFja2FnZXMsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNvd3Bsb3QpCmBgYAoKCiMgTG9hZCBwcmV2aW91c2x5IHNhdmVkIGRhdGFzZXRzIGFuZCBhZGQgYW5ub3RhdGlvbgpgYGB7cn0KY2RTMSA8LSByZWFkUkRTKCJ+L0RvY3VtZW50cy9FeHBlcmltZW50czEvc2NSTkFzZXEvQ2VyZWJlbGx1bV9wdWJsaWNhdGlvbi9hbGxDZWxscy9kYXRhL1NldXJhdDMucmRzIikKY2RTMUBtZXRhLmRhdGEkZGF0YXNldCA8LSAiRTEzIgpjb2xuYW1lcyhjZFMxQG1ldGEuZGF0YSlbMToyXSA8LSBjKCJuQ291bnRfUk5BIiwibkZlYXR1cmVfUk5BIikKCmNkUzIgPC0gcmVhZFJEUygiLi9FMTQvU2V1cmF0X0UxNC5yZHMiKQpjZFMzIDwtIHJlYWRSRFMoIi4vRTE1L1NldXJhdF9FMTUucmRzIikKY2RTNCA8LSByZWFkUkRTKCIuL0UxNi9TZXVyYXRfRTE2LnJkcyIpCgpjZFMxQG1ldGEuZGF0YSRkYXRhc2V0MSA8LSAiRTEzIgpjZFMyQG1ldGEuZGF0YSRkYXRhc2V0MSA8LSAiRTE0IgpjZFMzQG1ldGEuZGF0YSRkYXRhc2V0MSA8LSAiRTE1IgpjZFM0QG1ldGEuZGF0YSRkYXRhc2V0MSA8LSAiRTE2IgpgYGAKCgojIERhdGFzZXQgcHJlcHJvY2Vzc2luZwpgYGB7cn0KY2RTIDwtIG1lcmdlKGNkUzEsIGMoY2RTMiwgY2RTMywgY2RTNCkpCmNiLmxpc3QgPC0gU3BsaXRPYmplY3Qob2JqZWN0ID0gY2RTLCBzcGxpdC5ieSA9ICJkYXRhc2V0MSIpCmBgYAoKIyBOb3JtYWxpemUgZGF0YSBhbmQgZmluZCB2YXJpYWJsZSBnZW5lcwpgYGB7cn0KZm9yIChpIGluIDE6bGVuZ3RoKHggPSBjYi5saXN0KSkgewogIGNiLmxpc3RbW2ldXSA8LSBOb3JtYWxpemVEYXRhKG9iamVjdCA9IGNiLmxpc3RbW2ldXSwgdmVyYm9zZSA9IEZBTFNFKQogIGNiLmxpc3RbW2ldXSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBjYi5saXN0W1tpXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDAsIHZlcmJvc2UgPSBGQUxTRSkKfQpgYGAKCiMgSW50ZWdyYXRlIHRoZSBkZWF0YXNldHMKYGBge3J9CmFuY2hvcnMgPC0gRmluZEludGVncmF0aW9uQW5jaG9ycyhvYmplY3QubGlzdCA9IGNiLmxpc3QsIGRpbXMgPSAxOjMwKQpjZFMuaSA8LSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIGRpbXMgPSAxOjMwKQpgYGAKCiMgc3dpdGNoIHRvIGludGVncmF0ZWQgYXNzYXkKYGBge3J9CkRlZmF1bHRBc3NheShvYmplY3QgPSBjZFMuaSkgPC0gImludGVncmF0ZWQiCmBgYAoKIyBSdW4gdGhlIHN0YW5kYXJkIHdvcmtmbG93IGZvciB2aXN1YWxpemF0aW9uIGFuZCBjbHVzdGVyaW5nCmBgYHtyfQpjZFMuaSA8LSBTY2FsZURhdGEob2JqZWN0ID0gY2RTLmksIHZlcmJvc2UgPSBGQUxTRSkKY2RTLmkgPC0gUnVuUENBKG9iamVjdCA9IGNkUy5pLCBucGNzID0gNDAsIHZlcmJvc2UgPSBGQUxTRSkKRWxib3dQbG90KGNkUy5pLCBuZGltcyA9IDQwLCByZWR1Y3Rpb24gPSAicGNhIikKYGBgCgojIEZpbmQgY2x1c3RlcnMKYGBge3J9CmNkUy5pIDwtIEZpbmROZWlnaGJvcnMoY2RTLmksIHJlZHVjdGlvbiA9ICJwY2EiLCBhc3NheSA9ICJpbnRlZ3JhdGVkIiwgZGltcyA9IDE6MjMpCmBgYAoKCmBgYHtyfQpyZXMgPC0gc2VxKDAuNSw0LjAsMC41KQpjZFMuaSA8LSBGaW5kQ2x1c3RlcnMoY2RTLmksIG1vZHVsYXJpdHkuZnhuID0gMSwKICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9uID0gcmVzLCBhbGdvcml0aG0gPSAxLCBuLnN0YXJ0ID0gMTAsIG4uaXRlciA9IDEwLAogICAgICAgICAgICAgICAgICAgIHJhbmRvbS5zZWVkID0gMCwgdGVtcC5maWxlLmxvY2F0aW9uID0gTlVMTCwgZWRnZS5maWxlLm5hbWUgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGKQpgYGAKCiMgSW5zcGVjdCBjZWxsIGNsdXN0ZXJzCmBgYHtyIGZpZy53aWR0aD00LCBmaWcuYXNwPTJ9Cm15cGxvdHMgPC0gbGlzdCgpCmZvciAobiBpbiAxOmxlbmd0aChyZXMpKXsKICByZXMxIDwtIHBhc3RlKCJpbnRlZ3JhdGVkX3Nubl9yZXMiLHJlc1tuXSxzZXA9Ii4iKQogIElkZW50cyhvYmplY3QgPSBjZFMuaSkgPC0gY2RTLmlAbWV0YS5kYXRhWyxyZXMxXQogIG15cGxvdHNbW25dXSA8LSBEaW1QbG90KGNkUy5pLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImlkZW50IiwgbGFiZWw9VCkrCiAgICBOb0F4ZXMoKStOb0xlZ2VuZCgpCn0KbXlwbG90czEgPC0gbGlzdCgpCmZvciAobiBpbiAxOmxlbmd0aChyZXMpKXsKICByZXMxIDwtIHBhc3RlKCJpbnRlZ3JhdGVkX3Nubl9yZXMiLHJlc1tuXSxzZXA9Ii4iKQogIElkZW50cyhvYmplY3QgPSBjZFMuaSkgPC0gY2RTLmlAbWV0YS5kYXRhWyxyZXMxXQogIG15cGxvdHMxW1tuXV0gPC0gRGltUGxvdChjZFMuaSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsPVQpKwogICAgTm9BeGVzKCkrTm9MZWdlbmQoKQp9CgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icz0gbXlwbG90cywgbmNvbCA9IDIsIHRvcCA9IHBhc3RlMCgicmVzb2x1dGlvbnM6ICIsIHJlcykpCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzPSBteXBsb3RzMSwgbmNvbCA9IDIsIHRvcCA9IHBhc3RlMCgicmVzb2x1dGlvbnM6ICIsIHJlcykpCmBgYAoKYGBge3J9CnJlczEgPC0gcGFzdGUoImludGVncmF0ZWRfc25uX3JlcyIsMixzZXA9Ii4iKQpJZGVudHMob2JqZWN0ID0gY2RTLmkpIDwtIGNkUy5pQG1ldGEuZGF0YVsscmVzMV0KYGBgCgoKIyBWaXN1YWxpemUgdGhlIHJlc3VsdHMgd2l0aCB0LVNORQpgYGB7cn0KY2RTLmkgPC0gUnVuVFNORShjZFMuaSwgcmVkdWN0aW9uID0gInBjYSIsIGNlbGxzID0gTlVMTCwKICAgICAgICAgICAgICAgZGltcyA9IDE6MjMsIGZlYXR1cmVzID0gTlVMTCwgc2VlZC51c2UgPSAxLCB0c25lLm1ldGhvZCA9ICJSdHNuZSIsCiAgICAgICAgICAgICAgIGFkZC5pdGVyID0gMCwgZGltLmVtYmVkID0gMiwgZGlzdGFuY2UubWF0cml4ID0gTlVMTCwKICAgICAgICAgICAgICAgcmVkdWN0aW9uLm5hbWUgPSAidHNuZSIsIHJlZHVjdGlvbi5rZXkgPSAidFNORV8iLCBwZXJwbGV4aXR5ID0gNTApCkRpbVBsb3QoY2RTLmksIHJlZHVjdGlvbiA9ICJ0c25lIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBsYWJlbD1UKQpgYGAKCiMgVmlzdWFsaXplIHRoZSByZXN1bHRzIHdpdGggVU1BUApgYGB7cn0KY2RTLmkgPC0gUnVuVU1BUChvYmplY3QgPSBjZFMuaSwgZGltcyA9IDE6MjMsIHJlZHVjdGlvbiA9ICJwY2EiLAogICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBOVUxMLCBhc3NheSA9ICJpbnRlZ3JhdGVkIiwgbm5laWdoYm9ycyA9IDY1TCwgbWF4LmRpbSA9IDJMLAogICAgICAgICAgICAgICAgICAgICAgbWluLmRpc3QgPSAwLjQsIHJlZHVjdGlvbi5uYW1lID0gInVtYXAiLCByZWR1Y3Rpb24ua2V5ID0gIlVNQVBfIiwKICAgICAgICAgICAgICAgICAgICAgIG1ldHJpYyA9ICJjb3JyZWxhdGlvbiIsIHNlZWQudXNlID0gNDIpCkRpbVBsb3QoY2RTLmksIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBsYWJlbD1UKQpgYGAKCiMgVmlzdWFsaXplIHRoZSByZXN1bHRzIHdpdGggVU1BUApgYGB7cn0KY2RTLmkgPC0gUnVuVU1BUChvYmplY3QgPSBjZFMuaSwgZGltcyA9IDE6MjMsIHJlZHVjdGlvbiA9ICJwY2EiLAogICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBOVUxMLCBhc3NheSA9ICJpbnRlZ3JhdGVkIiwgbm5laWdoYm9ycyA9IDY1TCwgbWF4LmRpbSA9IDJMLAogICAgICAgICAgICAgICAgICAgICAgbWluLmRpc3QgPSAwLjQsIHJlZHVjdGlvbi5uYW1lID0gInVtYXAiLCByZWR1Y3Rpb24ua2V5ID0gIlVNQVBfIiwKICAgICAgICAgICAgICAgICAgICAgIG1ldHJpYyA9ICJjb3JyZWxhdGlvbiIsIHNlZWQudXNlID0gNDIpCkRpbVBsb3QoY2RTLmksIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBsYWJlbD1UKQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KRGltUGxvdChjZFMuaSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJjZWxsVHlwZSIsIGxhYmVsPVQsIHNwbGl0LmJ5ID0gImRhdGFzZXQxIikrCiAgTm9BeGVzKCkrTm9MZWdlbmQoKQpgYGAKCmBgYHtyIGZpZy5hc3A9MX0KRGltUGxvdChjZFMuaSwgcmVkdWN0aW9uID0gInRzbmUiLCBncm91cC5ieSA9ICJjZWxsVHlwZSIsIGxhYmVsPVQsIHNwbGl0LmJ5ID0gImRhdGFzZXQxIikrCiAgTm9BeGVzKCkrTm9MZWdlbmQoKQpgYGAKCmBgYHtyfQpwMSA8LSBEaW1QbG90KGNkUy5pLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImNlbGxUeXBlIiwgbGFiZWw9VCwgc3BsaXQuYnkgPSAiZGF0YXNldDEiLCByZXBlbCA9IFQpKwogIE5vQXhlcygpK05vTGVnZW5kKCkKcDIgPSBEaW1QbG90KGNkUy5pLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImNlbGxUeXBlIiwgbGFiZWw9VCwgc3BsaXQuYnkgPSAiZGF0YXNldDEiLCByZXBlbCA9IFQpKwogIE5vQXhlcygpK05vTGVnZW5kKCkKCgoKcDMgPC0gRGltUGxvdChjZFMuaSwgcmVkdWN0aW9uID0gInRzbmUiLCBncm91cC5ieSA9ICJkYXRhc2V0MSIsIGxhYmVsPUYpKwogIE5vQXhlcygpCnA0IDwtIERpbVBsb3QoY2RTLmksIHJlZHVjdGlvbiA9ICJ0c25lIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBsYWJlbD1UKSsKICBOb0F4ZXMoKStOb0xlZ2VuZCgpCnA1ID0gY293cGxvdDo6cGxvdF9ncmlkKHAzLHA0LCByZWxfd2lkdGhzID0gYygxLDAuOCkpCgpwNiA8LSBEaW1QbG90KGNkUy5pLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImRhdGFzZXQxIiwgbGFiZWw9RikrCiAgTm9BeGVzKCkKcDcgPC0gRGltUGxvdChjZFMuaSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsPVQpKwogIE5vQXhlcygpK05vTGVnZW5kKCkKcDggPSBjb3dwbG90OjpwbG90X2dyaWQocDYscDcsIHJlbF93aWR0aHMgPSBjKDEsMC44KSkKCmBgYAoKYGBge3J9CnBkZigic3VtbWFyeS5wZGYiLCBoPTE1LCB3PTEwKQpjb3dwbG90OjpwbG90X2dyaWQocDUscDEsIG5jb2wgPTEsIHJlbF9oZWlnaHRzID0gYygwLjksMikpCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwOCxwMiwgbmNvbCA9MSwgcmVsX2hlaWdodHMgPSBjKDAuOSwyKSkKZGV2Lm9mZigpCmBgYAoKCgpgYGB7cn0KcDEgPC0gRGltUGxvdChjZFMuaSwgcmVkdWN0aW9uID0gInRzbmUiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsPVQpICsKICBOb0F4ZXMoKStOb0xlZ2VuZCgpCnAyIDwtIERpbVBsb3QoY2RTLmksIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBsYWJlbD1UKSsKICBOb0F4ZXMoKStOb0xlZ2VuZCgpCnAzIDwtIERpbVBsb3QoY2RTLmksIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiZGF0YXNldDEiLCBsYWJlbD1GKQpwNCA8LSBEaW1QbG90KGNkUy5pLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImNlbGxUeXBlIiwgbGFiZWw9VCkrCiAgTm9BeGVzKCkrTm9MZWdlbmQoKQoKY293cGxvdDo6cGxvdF9ncmlkKHAxLHAyLHAzLHA0KQpgYGAKCgoKYGBge3IgZmlnLndpZHRoPTgsIGZpZy5hc3A9MS41fQpwNSA8LSBEaW1QbG90KGNkUy5pLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImNlbGxUeXBlIiwgcmVwZWwgPSBULCBsYWJlbD1ULCBzcGxpdC5ieSA9ICJkYXRhc2V0MSIpICsKICBOb0F4ZXMoKStOb0xlZ2VuZCgpCnA2IDwtIERpbVBsb3QoY2RTLmksIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiY2VsbFR5cGUiLCByZXBlbCA9IFQsIGxhYmVsPVQsIHNwbGl0LmJ5ID0gImRhdGFzZXQxIikrCiAgTm9BeGVzKCkrTm9MZWdlbmQoKQpjb3dwbG90OjpwbG90X2dyaWQocDUscDYsIG5jb2wgPTEpCmBgYAoKIyBTYXZlIHRoZSBTZXVyYXQgb2JqZWN0CmBgYHtyfQpzYXZlUkRTKGZpbGUgPSAiU2V1cmF0X21lcmdlLnJkcyIsY2RTLmkpCmBgYAoKCmBgYHtyfQptR2VuZXMgPC0gcmVhZFJEUyhmaWxlPSIvVm9sdW1lcy9qYWxpL0dlbm9tZS9Bbm5vdGF0aW9uMS5yZHMiKQpzaWdfZ2VuZXMgPC0gRmluZEFsbE1hcmtlcnMoY2RTLmksIGZlYXR1cmVzID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUsIHRlc3QudXNlID0gIndpbGNveCIsIG1pbi5wY3QgPSAwLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZGlmZi5wY3QgPSAtSW5mLCB2ZXJib3NlID0gRiwgb25seS5wb3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LmNlbGxzLnBlci5pZGVudCA9IEluZiwgcmFuZG9tLnNlZWQgPSAxLCBsYXRlbnQudmFycyA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY2VsbHMuZmVhdHVyZSA9IDMsIG1pbi5jZWxscy5ncm91cCA9IDMsIHBzZXVkb2NvdW50LnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4udGhyZXNoID0gMC4wMSkKCnNpZ19nZW5lcyA8LSBtdXRhdGUoc2lnX2dlbmVzLHBjdC5kaWZmPXBjdC4xLXBjdC4yKQoKc2lnX2dlbmVzJFRGIDwtICJubyIKaWQgPC0gaW50ZXJzZWN0KG1HZW5lcyRtZ2lfc3ltYm9sLHNpZ19nZW5lcyRnZW5lKQoKeCA8LSBzaWdfZ2VuZXNbc2lnX2dlbmVzJGdlbmUgJWluJSBpZCxdCnNpZ19nZW5lcyRURltzaWdfZ2VuZXMkZ2VuZSAlaW4lIGlkXSA8LSBtR2VuZXMkVHlwZVttYXRjaCh4JGdlbmUsbUdlbmVzJG1naV9zeW1ib2wpXQpzaWdfZ2VuZXMkZGVzY3JpcHRpb24gPC0gbUdlbmVzJGRlc2NyaXB0aW9uW21hdGNoKGFzLmNoYXJhY3RlcihzaWdfZ2VuZXMkZ2VuZSksbUdlbmVzJG1naV9zeW1ib2wpXQpoZWFkKHNpZ19nZW5lcykKCnNpZ19nZW5lcyA8LSBzaWdfZ2VuZXNbLCBjKCJnZW5lIiwiVEYiLCJkZXNjcmlwdGlvbiIsInBfdmFsIiwicF92YWxfYWRqIiwiYXZnX2xvZ0ZDIiwicGN0LjEiLCJwY3QuMiIsInBjdC5kaWZmIiwiY2x1c3RlciIpXQpvcGVueGxzeDo6d3JpdGUueGxzeChzaWdfZ2VuZXMsInNpZ0dlbmVzLnhsc3giKQoKdG9wIDwtIHNpZ19nZW5lcyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKDUsIGF2Z19sb2dGQyk7dG9wCnRhYiA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKElkZW50cyhvYmplY3QgPSBjZFMuaSkpKQp0b3AkY2VsbE51bWJlciA8LSB0YWIkRnJlcVttYXRjaCh0b3AkY2x1c3Rlcix0YWIkVmFyMSldCnRvcCRwZXJjZW50YWdlIDwtIHJvdW5kKHRvcCRjZWxsTnVtYmVyL25jb2woY2RTLmkpKjEwMCwgZGlnaXRzID0gMikKb3Blbnhsc3g6OndyaXRlLnhsc3godG9wLCJzaWdHZW5lc190b3A1Lnhsc3giKQpgYGAKCiMgQW5ub3RhdGUgY2VsbCBjbHVzdGVyCkFubm90YXRlIHRoZSBjZWxsIGN1bHN0ZXIgaW4gRXhjZWwuCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuYXNwPTF9CnRvcDUgPC0gb3Blbnhsc3g6OnJlYWQueGxzeCgic2lnR2VuZXNfc3VtLnhsc3giLCBzaGVldCA9MikKdG9wNWEgPC0gdG9wNVshZHVwbGljYXRlZCh0b3A1JGNsdXN0ZXIpLF0KdG9wNWEkY2VsbFR5cGUKCmNkUy5pQG1ldGEuZGF0YSRjZWxsVHlwZTEgPC0gcGx5cjo6bWFwdmFsdWVzKHggPSBJZGVudHMoY2RTLmkpLCBmcm9tID0gdG9wNWEkY2x1c3RlciwgdG8gPSB0b3A1YSRjZWxsVHlwZSkKRGltUGxvdChjZFMuaSwgcmVkdWN0aW9uID0gInRzbmUiLCBncm91cC5ieSA9ICJjZWxsVHlwZTEiLCBsYWJlbD1ULCByZXBlbCA9IFQpKwogIE5vTGVnZW5kKCkKRGltUGxvdChjZFMuaSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJjZWxsVHlwZTEiLCBsYWJlbD1ULCByZXBlbCA9IFQpKwogIE5vTGVnZW5kKCkKCmBgYAoKYGBge3J9CnRhYmxlKGNkUy5pQG1ldGEuZGF0YSRjZWxsVHlwZTEsIGNkUy5pQG1ldGEuZGF0YSRkYXRhc2V0MSkKYGBgCgoKYGBge3J9CnAxIDwtIERpbVBsb3QoY2RTLmksIHJlZHVjdGlvbiA9ICJ0c25lIiwgZ3JvdXAuYnkgPSAiY2VsbFR5cGUxIiwgbGFiZWw9VCwgc3BsaXQuYnkgPSAiZGF0YXNldDEiLCByZXBlbCA9IFQpKwogIE5vQXhlcygpK05vTGVnZW5kKCkKcDIgPSBEaW1QbG90KGNkUy5pLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImNlbGxUeXBlMSIsIGxhYmVsPVQsIHNwbGl0LmJ5ID0gImRhdGFzZXQxIiwgcmVwZWwgPSBUKSsKICBOb0F4ZXMoKStOb0xlZ2VuZCgpCgpwMyA8LSBEaW1QbG90KGNkUy5pLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImRhdGFzZXQxIiwgbGFiZWw9RikrCiAgTm9BeGVzKCkKcDQgPC0gRGltUGxvdChjZFMuaSwgcmVkdWN0aW9uID0gInRzbmUiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsPVQpKwogIE5vQXhlcygpK05vTGVnZW5kKCkKcDUgPSBjb3dwbG90OjpwbG90X2dyaWQocDMscDQsIHJlbF93aWR0aHMgPSBjKDEsMC44KSkKCnA2IDwtIERpbVBsb3QoY2RTLmksIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiZGF0YXNldDEiLCBsYWJlbD1GKSsKICBOb0F4ZXMoKQpwNyA8LSBEaW1QbG90KGNkUy5pLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImlkZW50IiwgbGFiZWw9VCkrCiAgTm9BeGVzKCkrTm9MZWdlbmQoKQpwOCA9IGNvd3Bsb3Q6OnBsb3RfZ3JpZChwNixwNywgcmVsX3dpZHRocyA9IGMoMSwwLjgpKQoKYGBgCgpgYGB7cn0KcGRmKCJzdW1tYXJ5MS5wZGYiLCBoPTE1LCB3PTEwKQpjb3dwbG90OjpwbG90X2dyaWQocDUscDEsIG5jb2wgPTEsIHJlbF9oZWlnaHRzID0gYygwLjksMikpCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwOCxwMiwgbmNvbCA9MSwgcmVsX2hlaWdodHMgPSBjKDAuOSwyKSkKZGV2Lm9mZigpCmBgYAoKIyBQcmludCBzZXNzdGlvbiBpbmZvcm1hdGlvbgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==